package cn.jiangzeyin.proxy;

import cn.hutool.cache.impl.CacheObj;
import cn.hutool.cache.impl.FIFOCache;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.CronUtil;
import cn.hutool.http.HttpUtil;
import cn.jiangzeyin.common.DefaultSystemLog;
import cn.jiangzeyin.pool.ThreadPoolService;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;

import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Created by jiangzeyin on 2018/9/25.
 *
 * @author jiangzeyin
 */
public class ProxyUtil {
    private static int CACHE_MAX_COUNT = 200;
    private static String key;
    private static final FIFOCache<String, JSONObject> FIFO_CACHE = new FIFOCache<>(CACHE_MAX_COUNT, TimeUnit.MINUTES.toMillis(5));
    private static final Method PRUNE_CACHE;
    private static final HashSet<String> HASH_SET = new HashSet<>();
    private static final ExecutorService EXECUTOR_SERVICE = ThreadPoolService.newCachedThreadPool(ProxyUtil.class);
    private static long previousTime;

    static {
        Method pruneCache1;
        URL url = ResourceUtil.getResource("proxy/orderId");
        key = FileUtil.readString(url, CharsetUtil.UTF_8);
        //
        url = ResourceUtil.getResource("proxy/whitelist");
        List<String> list = FileUtil.readLines(url, CharsetUtil.UTF_8);
        HASH_SET.addAll(list);
        //
        try {
            pruneCache1 = FIFOCache.class.getDeclaredMethod("pruneCache");
            pruneCache1.setAccessible(true);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            pruneCache1 = null;
        }
        PRUNE_CACHE = pruneCache1;
    }

    static JSONObject next() {
        Iterator<CacheObj<String, JSONObject>> iterator = FIFO_CACHE.cacheObjIterator();
        if (iterator.hasNext()) {
            CacheObj<String, JSONObject> cacheObj = iterator.next();
            FIFO_CACHE.remove(cacheObj.getKey());
            return cacheObj.getValue();
        }
        return null;
    }

    static boolean checkWhite(String ip) {
        if (HASH_SET.size() <= 0) {
            return true;
        }
        return HASH_SET.contains(ip);
    }

    static int getSize() {
        return FIFO_CACHE.size();
    }

    public static void init() {
        CronUtil.setMatchSecond(true);
        CronUtil.schedule("0/7 * * * * ?", (Runnable) () -> {
            long time = SystemClock.now() - previousTime;
            if (time < TimeUnit.SECONDS.toMillis(7)) {
                return;
            }
            try {
                getApi();
            } catch (Exception e) {
                e.printStackTrace();
            }
            previousTime = SystemClock.now();
        });
        CronUtil.start();
    }

    private static void getApi() throws InvocationTargetException, IllegalAccessException {
        if (PRUNE_CACHE != null) {
            Object object = PRUNE_CACHE.invoke(FIFO_CACHE);
            DefaultSystemLog.ERROR().info("清理：" + object);
        }
        int size = FIFO_CACHE.size();
        if (size >= CACHE_MAX_COUNT) {
            return;
        }
        Map<String, Object> map = new HashMap<>(8);
        map.put("key", key);
        //  每次获取总数的  0.2 倍
        map.put("getnum", CACHE_MAX_COUNT * 0.2);
        // 超高匿名
        map.put("anonymoustype", 4);
        map.put("order", 2);
        map.put("formats", 2);
        // 国内
        map.put("area", 1);
        //  http  和 https
        map.put("proxytype", "01");
        String json = HttpUtil.createGet("http://gea.ip3366.net/api/").
                form(map).
                charset(CharsetUtil.GBK).
                execute().body();
        JSONArray jsonArray;
        try {
            jsonArray = JSONArray.parseArray(json);
        } catch (JSONException exception) {
            DefaultSystemLog.ERROR().error("json错误" + json);
            return;
        }
        if (jsonArray == null) {
            return;
        }
        jsonArray.forEach(o -> {
            JSONObject jsonObject = (JSONObject) o;
            String ip = jsonObject.getString("Ip");
            if (StrUtil.isEmpty(ip)) {
                return;
            }
            int port = jsonObject.getIntValue("Port");
            if (port <= 0) {
                return;
            }
            EXECUTOR_SERVICE.execute(() -> {
                if (isConnection(ip, port)) {
                    String info = ip + ":" + port;
                    FIFO_CACHE.put(info, jsonObject);
                }
            });
        });
    }

    private static boolean isConnection(String ip, int port) {
        try {
            //Proxy类代理方法
            URL url = new URL("http://word-robot.ztoutiao.cn/proxy/showIp");
            // 创建代理服务器
            InetSocketAddress addr = new InetSocketAddress(ip, port);
            // http 代理
            Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);
            URLConnection conn = url.openConnection(proxy);
            InputStream in = conn.getInputStream();
            String s = IoUtil.read(in, CharsetUtil.UTF_8);
            if (!Validator.isIpv4(s)) {
                return false;
            }
            return !ip.equals(s);
        } catch (Exception e) {
            return false;
        }
    }
}
